﻿using System.Collections.Generic;
using TextEditor.TextBoxInner.DrawingElements;
using TextEditor.TextBoxInner.Utils;

namespace TextEditor.TextBoxInner.Layers
{
	internal class SelectionLayer : LayerBase<SelectionLine>
	{
		private static readonly SelectionLineCreator _selectionLineCreator = new SelectionLineCreator();

		private readonly List<SelectionLine> _tempInvisibleList = new List<SelectionLine>(50);
		private readonly List<SelectionLine> _tempCandidateseList = new List<SelectionLine>(50);

		public SelectionLayer() : base(_selectionLineCreator)
		{ }

		public void SetLines(SelectionLineInfo[] lines)
		{
			try
			{
				if (lines.Length == 0)
				{
					for (int index = 0; index < Children.Count; index++)
						Children[index].ClearContent();

					return;
				}

				_tempInvisibleList.Clear();
				_tempCandidateseList.Clear();

				int startIndex = lines[0].LineIndex;
				int endIndex = lines[lines.Length - 1].LineIndex;

				int startCandidatesIndex = -1;
//				int endCandidatesIndex = -1;

				for (int index = 0; index < Children.Count; index++)
				{
					SelectionLine line = Children[index];

					if (!line.IsVisible)
					{
						_tempInvisibleList.Add(line);
						continue;
					}

					if (line.LineIndex < startIndex)
					{
						_tempInvisibleList.Add(line);
						continue;
					}

					if (line.LineIndex > endIndex)
					{
						_tempInvisibleList.Add(line);
						continue;
					}

					_tempCandidateseList.Add(line);

//					endCandidatesIndex = line.LineIndex;

					if (startCandidatesIndex < 0)
						startCandidatesIndex = line.LineIndex;
				}

				for (int index = 0; index < lines.Length; index++)
				{
					SelectionLineInfo item = lines[index];
					int itemIndex = item.LineIndex;

					SelectionLine targetLine;

					if (itemIndex == startCandidatesIndex && _tempCandidateseList.Count > 0)
					{
						Children[index] = targetLine = _tempCandidateseList[0];
						_tempCandidateseList.RemoveAt(0);
						startCandidatesIndex++;
					}
					else
					{
						int tempElementIndex = _tempInvisibleList.Count - 1;
						targetLine = _tempInvisibleList[tempElementIndex];
						_tempInvisibleList.RemoveAt(tempElementIndex);

						Children[index] = targetLine;
					}

					if (item.IsEmpty)
						targetLine.ClearContent();
					else
						targetLine.SetContent(item.LineIndex, item.StartCol, item.EndCol);
				}

				for (int index = lines.Length; index < Children.Count; index++)
				{
					int tempElementIndex = _tempInvisibleList.Count - 1;
					SelectionLine temp = _tempInvisibleList[tempElementIndex];
					_tempInvisibleList.RemoveAt(tempElementIndex);

					Children[index] = temp;
					temp.ClearContent();
				}

				for (int index = 0; index < Children.Count; index++)
					Children[index].SetTop(index * FontHelper.FontHeight);
			}
			finally
			{
				InvalidateArrange();
			}
		}

		private class SelectionLineCreator : DrawingElementFactory<SelectionLine>
		{
			internal override SelectionLine Create()
			{
				return new SelectionLine();
			}
		}
	}

	internal struct SelectionLineInfo
	{
		private readonly bool _isEmpty;
		private readonly int _line;
		private readonly int _startCol;
		private readonly int _endCol;

		internal static SelectionLineInfo CreateEmptySelection(int line)
		{
			return new SelectionLineInfo(true, line, -1, -1);
		}

		public SelectionLineInfo(int line, int startCol, int endCol)
			: this(false, line, startCol, endCol)
		{ }

		private SelectionLineInfo(bool isEmpty, int line, int startCol, int endCol)
		{
			_isEmpty = isEmpty;
			_line = line;
			_startCol = startCol;
			_endCol = endCol;
		}

		public bool IsEmpty
		{
			get { return _isEmpty; }
		}

		public int LineIndex
		{
			get { return _line; }
		}

		public int StartCol
		{
			get { return _startCol; }
		}

		public int EndCol
		{
			get { return _endCol; }
		}

		public override string ToString()
		{
			return _isEmpty ? "Empty" : string.Format("{0} - {1}", _startCol, _endCol);
		}
	}
}
